#include "map.h"



mapa* mapa::get_instance()
{
	static mapa inst( 0 );
	return &inst;
};



void mapa::rebuild()
{
	if ( objects != 0 )
	{
		for ( unsigned int i = 0; i < size_x; ++i )
			for ( unsigned int j = 0; j < size_y; ++j )
				if ( dynamic_cast< bonus* >( objects->get( i, j ) ) != 0 )
					delete objects->get( i, j );
		delete objects;
	}
	if ( terrain != 0 )
		delete terrain;
	objects = 0;
	terrain = 0;
};



mapa::mapa( int _x ):x( _x )
{
	objects = 0;
	terrain = 0;
};



mapa::~mapa()
{
	if ( objects != 0 )
	{
		for ( unsigned int i = 0; i < size_x; ++i )
			for ( unsigned int j = 0; j < size_y; ++j )
				if ( dynamic_cast< bonus* >( objects->get( i, j ) ) != 0 )
					delete objects->get( i, j );
		delete objects;
	}
	if ( terrain != 0 )
		delete terrain;
	objects = 0;
	terrain = 0;
};



void mapa::clean_map()
{
	for ( unsigned int i = 0; i < size_x; ++i )
		for ( unsigned int j = 0; j < size_y; ++j )
			if ( dynamic_cast< bonus* >( objects->get( i, j ) ) != 0 )
			{
				delete objects->get( i, j );
				objects->set( 0, i, j );
			}
};



void mapa::set_object( object* o, unsigned int x, unsigned int y )
{
	if ( objects != 0 )
		objects->set( o, x, y );
};



object* mapa::get_object( unsigned int x, unsigned int y )
{
	if ( objects != 0 )
		return objects->get( x, y );
	else
		return 0;
};



bool mapa::load( istream& is )
{
	if ( !is.good() )
	{
		cout << "ERROR WHILE OPENING MAP FILE" << endl;
		return false;
	}
	is >> size_x;
	is >> size_y;
	if ( !is.good() )
	{
		cout << "ERROR WHILE READING MAP FILE" << endl;
		return false;
	}
	terrain = new array2d< unsigned int >( size_x, size_y );
	for ( unsigned int i = 0; i < size_y; ++i )
		for ( unsigned int j = 0; j < size_x; ++j )
		{
			unsigned int t;
			is >> t;
			if ( t < 10 )
			{
				cout << "ERROR WHILE READING MAP FILE" << endl;
				delete terrain;
				return false;
			}
			if ( ( t > 109 ) && ( t != 20000 ) && ( t != 30000 ) )
			{
				cout << "ERROR WHILE READING MAP FILE" << endl;
				delete terrain;
				return false;
			}
			if ( t > 30000 )
			{
				cout << "ERROR WHILE READING MAP FILE" << endl;
				delete terrain;
				return false;
			}
			terrain->set( t, j, i );
			if ( is.fail() )
			{
				cout << "ERROR WHILE READING MAP FILE" << endl;
				delete terrain;
				return false;
			}
		}	
	objects = new array2d< object* >( size_x, size_y );
	for ( unsigned int i = 0; i < size_x; ++i )
		for( unsigned int j = 0; j < size_y; ++j )
		{
			objects->set( 0, i, j );			
		}	
	return true;
};



bool mapa::load_objects( istream& is )
{
	if ( !is.good() )
	{
		cout << "ERROR WHILE OPENING OBJECTS FILE" << endl;
		return false;
	}
	string objects_line;
	for ( ; ; )
	{
		getline( is, objects_line );
		if ( is.fail() )
		{
			break;
		}
		if ( !parse_objects_line ( objects_line ) )
		{
			cout << "ERROR WHILE READING OBJECTS FILE" << endl;
			return false;
		}
	}
	return true;
};



bool mapa::load( const char* file )
{
	ifstream fin;
	fin.open( file, ifstream::in );
	if ( load( fin ) )
	{
		fin.close();
		cout << "MAP LOADED SUCCESSFULLY" << endl;
		return true;
	}
	else
	{
		fin.close();
		return false;
	}
};



bool mapa::load_objects( const char* file )
{
	ifstream fin;
	fin.open( file, ifstream::in );
	if ( load_objects( fin ) )
	{
		fin.close();
		cout << "OBJECTS LOADED SUCCESSFULLY" << endl;
		return true;
	}
	else
	{
		fin.close();
		return false;
	}
};



bool mapa::parse_objects_line( const string& ol )
{
	if ( ol[0] == '#' )
		return true;

	istringstream sstr( ol );

	string type;
	sstr >> type;
	if ( type == "" )
		return true;

	sstr.ignore( ol.size(), '"' );
	string name;
	getline( sstr, name, '"' );

	unsigned int px;
	unsigned int py;
	unsigned int health;
	unsigned int pl_id;

	sstr >> px;
	sstr >> py;

	if ( px >= mapa::get_instance()->get_size_x() )
		return false;
	if ( py >= mapa::get_instance()->get_size_x() )
		return false;
	

	if ( sstr.fail() )
		return false;

	if ( ( terrain->get( px, py ) < 20000 ) && ( get_object( px, py ) == 0 ) )
	{
		players* plrs = players::get_instance();
		prototypes* proto = prototypes::get_instance();

		if ( type == "UNIT" )
		{
			sstr >> health;
			sstr >> pl_id;
			if ( sstr.fail() )
				return false;
			if ( ( pl_id < parameters::get_instance()->game_parameters[ "MAX_PL" ] ) && ( plrs->get_player( pl_id )->units.size() < parameters::get_instance()->game_parameters[ "MAX_UNITS" ] ) )
			{
				unit* nu = dynamic_cast< unit* >( proto->units[ name ]->clone() );
				nu->init( px, py, plrs->get_player( pl_id ), ( ( -1 ) * ( plrs->get_player( pl_id )->units.size() + 1 ) ) );
				nu->set_health( health );
				plrs->get_player( pl_id )->units.push_back( nu );
				objects->set( nu, px, py );
				cout << "LOADED UNIT: " << name << "\t[" << px << "," << py << "]" << endl;
			}
		}
		else if ( type == "BONUS" )
		{
			bonus* nb = dynamic_cast< bonus* >( proto->bonuses[ name ]->clone() );
			nb->init( px, py );
			objects->set( nb, px, py );
			cout << "LOADED BONUS: " << name << "\t[" << px << "," << py << "]" << endl;
		}
		else
		{
			return false;
		}
	}
	return true;
};



void mapa::save_objects( const char* file )
{
	ofstream fout;
	fout.open( file, ios::trunc );
	if ( fout.good() )
	{
		fout << "#\tType\tName\tPos_x\tPos_y\tHealth\tPlayer_id" << endl << endl;
		for ( unsigned int p = 0; p < players::get_instance()->get_players_count(); ++p )
			for( vector< unit* >::iterator it = players::get_instance()->get_player( p )->units.begin(); it != players::get_instance()->get_player( p )->units.end(); ++it )
			{
				fout << "UNIT\t\"" << ( *it )->get_name() << "\"\t" << ( *it )->get_x() << "\t" << ( *it )->get_y() << "\t" << ( *it )->get_health() << "\t" << ( *it )->get_player()->get_id() << endl;
			}
		for ( unsigned int i = 0; i < size_x; ++i )
			for( unsigned int j = 0; j < size_y; ++j )
			{
				bonus* b = dynamic_cast< bonus* >( objects->get( i, j ) );	
				if ( b != 0 )
				{
					fout << "BONUS\t\"" << b->get_name() << "\"\t" << b->get_x() << "\t" << b->get_y() << endl;
				}
			}
		fout.close();
		cout << "GAME SAVED SUCCESSFULLY" << endl;
	}
	else
	{
		cout << "ERROR WHILE SAVING GAME" << endl;
		fout.close();
	}
};



array2d< unsigned int >* mapa::terrain_clone()
{
	array2d< unsigned int >* nm = new array2d< unsigned int >( size_x, size_y );
	for( unsigned int i = 0; i < size_x; ++i )
		for( unsigned int j = 0; j < size_y; ++j )
			nm->set( terrain->get( i, j ), i, j );
	return nm;
};



void mapa::export_map( array2d< unsigned int >* map, const char* file, unsigned int sx, unsigned int sy )
{
	ofstream fout;
	fout.open( file, ios::trunc );
	if ( fout.good() )
	{
		for ( unsigned int i = 0; i < sx; ++i )
		{
			for( unsigned int j = 0; j < sy; ++j )
			{
				fout << map->get( j, i ) << "\t";			
			}
			fout << endl;
		}
		fout.close();
		cout << "MAP EXPORTED SUCCESSFULLY" << endl;
	}
	else
	{
		cout << "ERROR WHILE EXPORTING MAP" << endl;
		fout.close();
	}
};